package com.cms.compiler

import com.cms.annotation.ExtractField
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asTypeName
import java.io.File
import java.io.Serializable
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.Element
import javax.lang.model.element.ElementKind
import javax.lang.model.element.TypeElement
import javax.tools.Diagnostic

class Processor : AbstractProcessor() {


    companion object {
        const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated"

    }

    /***
     * 注解处理器处理的注解类型
     */
    override fun getSupportedAnnotationTypes(): MutableSet<String> {
        return mutableSetOf(ExtractField::class.java.name)
    }

    override fun getSupportedSourceVersion(): SourceVersion {
        return SourceVersion.latestSupported()
    }

    override fun process(
        annotations: MutableSet<out TypeElement>?,
        roundEnvironment: RoundEnvironment?,
    ): Boolean {
        processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, "开始执行注解")
        /****
         * 扫描代码中的注解
         */
        val elementMutableSet = roundEnvironment?.getElementsAnnotatedWith(ExtractField::class.java)
        if (elementMutableSet.isNullOrEmpty()) return false
        elementMutableSet.forEach {
            println("TAG1111--->${it.kind}")
            if (it is TypeElement){
                //判断注解未作用到类上
                if (it.kind != ElementKind.CLASS) {
                    processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, "未使用注解")
                    return@forEach
                }
                processAnnotation(it)
            }

        }


        return true
    }

    private fun processAnnotation(element: Element) {
        //获取类名
        val className = element.simpleName.toString()
        //获取包名
        val pack = processingEnv.elementUtils.getPackageOf(element).toString()
        //定义文件名称
        val fileName = "ExtractFile$className"
        //表示kotlin文件
        val fileBuilder = FileSpec.builder(pack, className)
        //即将生成的类
        val classBuilder = TypeSpec.classBuilder(fileName)
            .addSuperinterface(Serializable::class.java)//继承接口
            .addModifiers(KModifier.PUBLIC) //public

        //遍历查找注解
        for (classElement in element.enclosedElements) {
            if (classElement.kind == ElementKind.FIELD) {
                addProperty(classBuilder, classElement)
                addGetFunc(classBuilder, classElement)
                addSetFunc(classBuilder, classElement)
            }
        }
        val fileSpec = fileBuilder.addType(classBuilder.build()).build()
        val kaptDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
        fileSpec.writeTo(File(kaptDir))

    }

    /**
     * 增加类属性
     */
    private fun addProperty(classBuilder: TypeSpec.Builder, classElement: Element) {
       classBuilder.addProperty(PropertySpec.builder(classElement.simpleName.toString(),classElement.asType().asTypeName().copy(nullable = true)
       ,KModifier.PRIVATE)//私有属性
           .mutable()//参数var
           .initializer("null").build())
    }

    /**
     * 增加get方法
     */
    private fun addGetFunc(classBuilder: TypeSpec.Builder, classElement: Element) {
         classBuilder.addFunction(FunSpec.builder("getThe${classElement.simpleName}")
             .returns(classElement.asType().asTypeName().copy(nullable = true))

             .addStatement("return ${classElement.simpleName}").build())
    }

    /**
     * 增加set方法
     */

    private fun addSetFunc(classBuilder: TypeSpec.Builder, classElement: Element) {
         classBuilder.addFunction(
             FunSpec.builder("setThe${classElement.simpleName}")
                 .addParameter(
                 ParameterSpec.builder("${classElement.simpleName}",classElement.asType().asTypeName().copy(nullable = true)).build()
         ).addStatement("this.${classElement.simpleName} = ${classElement.simpleName}").build())
    }

}